import { BaseMiddleware, MiddlewareContext } from './base';
import { MessageProcessResult } from '@/core/message';
import { Logger } from '@/types';
import { createLogger } from '@/utils/logger';

/**
 * Logging middleware configuration
 */
export interface LoggingMiddlewareConfig {
  logSend?: boolean;
  logProcess?: boolean;
  logErrors?: boolean;
  logLevel?: 'debug' | 'info' | 'warn' | 'error';
  includeMessageBody?: boolean;
  includeHeaders?: boolean;
  includeProperties?: boolean;
  maxBodyLength?: number;
}

/**
 * Logging middleware for message operations
 */
export class LoggingMiddleware extends BaseMiddleware {
  private readonly config: Required<LoggingMiddlewareConfig>;

  constructor(config: LoggingMiddlewareConfig = {}, logger?: Logger) {
    super('LoggingMiddleware', logger ?? createLogger('LoggingMiddleware'));

    this.config = {
      logSend: true,
      logProcess: true,
      logErrors: true,
      logLevel: 'info',
      includeMessageBody: false,
      includeHeaders: true,
      includeProperties: true,
      maxBodyLength: 1000,
      ...config,
    };
  }

  async beforeSend(context: MiddlewareContext): Promise<MiddlewareContext> {
    if (this.config.logSend) {
      const logData = this.buildLogData(context, 'beforeSend');
      this.log(this.config.logLevel, `Sending message ${context.message.id}`, logData);
    }
    return context;
  }

  async afterSend(context: MiddlewareContext, result: boolean): Promise<void> {
    if (this.config.logSend) {
      const duration = Date.now() - context.startTime;
      const logData = this.buildLogData(context, 'afterSend', { result, duration });

      if (result) {
        this.log(this.config.logLevel, `Message ${context.message.id} sent successfully`, logData);
      } else {
        this.log('warn', `Message ${context.message.id} send failed`, logData);
      }
    }
  }

  async beforeProcess(context: MiddlewareContext): Promise<MiddlewareContext> {
    if (this.config.logProcess) {
      const logData = this.buildLogData(context, 'beforeProcess');
      this.log(this.config.logLevel, `Processing message ${context.message.id}`, logData);
    }
    return context;
  }

  async afterProcess(context: MiddlewareContext, result: MessageProcessResult): Promise<void> {
    if (this.config.logProcess) {
      const duration = Date.now() - context.startTime;
      const logData = this.buildLogData(context, 'afterProcess', {
        success: result.success,
        shouldRetry: result.shouldRetry,
        duration,
        resultMessage: result.message,
      });

      if (result.success) {
        this.log(
          this.config.logLevel,
          `Message ${context.message.id} processed successfully`,
          logData
        );
      } else {
        this.log('warn', `Message ${context.message.id} processing failed`, logData);
      }
    }
  }

  async onError(context: MiddlewareContext, error: Error): Promise<void> {
    if (this.config.logErrors) {
      const duration = Date.now() - context.startTime;
      const logData = this.buildLogData(context, 'onError', {
        error: error.message,
        stack: error.stack,
        duration,
      });

      this.log('error', `Error processing message ${context.message.id}`, logData);
    }
  }

  private buildLogData(
    context: MiddlewareContext,
    phase: string,
    additional: Record<string, unknown> = {}
  ): Record<string, unknown> {
    const logData: Record<string, unknown> = {
      messageId: context.message.id,
      timestamp: context.message.timestamp,
      retryCount: context.message.retryCount,
      phase,
      ...additional,
    };

    if (this.config.includeHeaders) {
      logData.headers = context.message.headers;
    }

    if (this.config.includeProperties) {
      logData.properties = context.message.properties;
    }

    if (this.config.includeMessageBody) {
      let body = context.message.body;

      // Truncate body if it's too long
      if (typeof body === 'string' && body.length > this.config.maxBodyLength) {
        body = body.substring(0, this.config.maxBodyLength) + '...';
      } else if (typeof body === 'object' && body !== null) {
        const bodyStr = JSON.stringify(body);
        if (bodyStr.length > this.config.maxBodyLength) {
          body = bodyStr.substring(0, this.config.maxBodyLength) + '...';
        }
      }

      logData.body = body;
    }

    if (context.correlationId) {
      logData.correlationId = context.correlationId;
    }

    if (context.traceId) {
      logData.traceId = context.traceId;
    }

    if (context.spanId) {
      logData.spanId = context.spanId;
    }

    // Include custom metadata
    if (Object.keys(context.metadata).length > 0) {
      logData.metadata = context.metadata;
    }

    return logData;
  }
}

/**
 * Structured logging middleware with different log levels for different events
 */
export class StructuredLoggingMiddleware extends BaseMiddleware {
  private readonly config: {
    sendLevel: 'debug' | 'info' | 'warn' | 'error';
    processLevel: 'debug' | 'info' | 'warn' | 'error';
    errorLevel: 'debug' | 'info' | 'warn' | 'error';
    includeStackTrace: boolean;
  };

  constructor(
    config: {
      sendLevel?: 'debug' | 'info' | 'warn' | 'error';
      processLevel?: 'debug' | 'info' | 'warn' | 'error';
      errorLevel?: 'debug' | 'info' | 'warn' | 'error';
      includeStackTrace?: boolean;
    } = {},
    logger?: Logger
  ) {
    super('StructuredLoggingMiddleware', logger ?? createLogger('StructuredLoggingMiddleware'));

    this.config = {
      sendLevel: 'debug',
      processLevel: 'info',
      errorLevel: 'error',
      includeStackTrace: true,
      ...config,
    };
  }

  async beforeSend(context: MiddlewareContext): Promise<MiddlewareContext> {
    this.log(this.config.sendLevel, 'Message send started', {
      event: 'message.send.start',
      messageId: context.message.id,
      timestamp: new Date().toISOString(),
    });
    return context;
  }

  async afterSend(context: MiddlewareContext, result: boolean): Promise<void> {
    const duration = Date.now() - context.startTime;

    this.log(this.config.sendLevel, 'Message send completed', {
      event: 'message.send.complete',
      messageId: context.message.id,
      success: result,
      duration,
      timestamp: new Date().toISOString(),
    });
  }

  async beforeProcess(context: MiddlewareContext): Promise<MiddlewareContext> {
    this.log(this.config.processLevel, 'Message processing started', {
      event: 'message.process.start',
      messageId: context.message.id,
      retryCount: context.message.retryCount,
      timestamp: new Date().toISOString(),
    });
    return context;
  }

  async afterProcess(context: MiddlewareContext, result: MessageProcessResult): Promise<void> {
    const duration = Date.now() - context.startTime;

    this.log(this.config.processLevel, 'Message processing completed', {
      event: 'message.process.complete',
      messageId: context.message.id,
      success: result.success,
      shouldRetry: result.shouldRetry,
      duration,
      timestamp: new Date().toISOString(),
    });
  }

  async onError(context: MiddlewareContext, error: Error): Promise<void> {
    const duration = Date.now() - context.startTime;
    const errorData: Record<string, unknown> = {
      event: 'message.error',
      messageId: context.message.id,
      error: error.message,
      duration,
      timestamp: new Date().toISOString(),
    };

    if (this.config.includeStackTrace) {
      errorData.stack = error.stack;
    }

    this.log(this.config.errorLevel, 'Message processing error', errorData);
  }
}

/**
 * Performance logging middleware for tracking message processing times
 */
export class PerformanceLoggingMiddleware extends BaseMiddleware {
  private readonly slowThresholdMs: number;
  private readonly logAllMessages: boolean;

  constructor(slowThresholdMs = 1000, logAllMessages = false, logger?: Logger) {
    super('PerformanceLoggingMiddleware', logger ?? createLogger('PerformanceLoggingMiddleware'));
    this.slowThresholdMs = slowThresholdMs;
    this.logAllMessages = logAllMessages;
  }

  async afterSend(context: MiddlewareContext, result: boolean): Promise<void> {
    const duration = Date.now() - context.startTime;

    if (this.logAllMessages || duration > this.slowThresholdMs) {
      const level = duration > this.slowThresholdMs ? 'warn' : 'info';

      this.log(level, `Message send performance`, {
        messageId: context.message.id,
        duration,
        slow: duration > this.slowThresholdMs,
        success: result,
      });
    }
  }

  async afterProcess(context: MiddlewareContext, result: MessageProcessResult): Promise<void> {
    const duration = Date.now() - context.startTime;

    if (this.logAllMessages || duration > this.slowThresholdMs) {
      const level = duration > this.slowThresholdMs ? 'warn' : 'info';

      this.log(level, `Message processing performance`, {
        messageId: context.message.id,
        duration,
        slow: duration > this.slowThresholdMs,
        success: result.success,
        retryCount: context.message.retryCount,
      });
    }
  }
}

/**
 * Utility functions for creating logging middleware
 */
export const createLoggingMiddleware = (
  config?: LoggingMiddlewareConfig,
  logger?: Logger
): LoggingMiddleware => {
  return new LoggingMiddleware(config, logger);
};

export const createStructuredLoggingMiddleware = (
  config?: {
    sendLevel?: 'debug' | 'info' | 'warn' | 'error';
    processLevel?: 'debug' | 'info' | 'warn' | 'error';
    errorLevel?: 'debug' | 'info' | 'warn' | 'error';
    includeStackTrace?: boolean;
  },
  logger?: Logger
): StructuredLoggingMiddleware => {
  return new StructuredLoggingMiddleware(config, logger);
};

export const createPerformanceLoggingMiddleware = (
  slowThresholdMs = 1000,
  logAllMessages = false,
  logger?: Logger
): PerformanceLoggingMiddleware => {
  return new PerformanceLoggingMiddleware(slowThresholdMs, logAllMessages, logger);
};
